1658. 将 x 减到 0 的最小操作数
1658. 将 x 减到 0 的最小操作数
Similar Question
Solution Tips
方案一: 记忆化递归
var minOperations = function(nums, x) {
// 回溯法, 暴力处理, 每次都有2种选择, 移除一个头, 移除一个尾
// 滑动窗口? 怎么处理的? 贪心? 先删除最大的? 感觉没道理的
// 其实是 x 数之和? 从头尾找连续的构成 target ? 但是我的记忆中, 对撞指针需要是有序的
// 赶紧开始回溯吧, 不然都做不出来了
let res = Number.MAX_SAFE_INTEGER;
const map = {};
recursive(0, nums.length - 1, x, 0);
// 先减头, 后减尾, 以及先减去尾, 再减去头, 其实是一样的
// 记忆化递归还是超时了, 要从记忆化递归去思考如何优化
return res === Number.MAX_SAFE_INTEGER ? -1 : res;
function recursive(start, end, target, op) {
// 从 rest 的头尾中选一个, 继续递归
if (target === 0) {
res = Math.min(res, op)
return;
}
// nums[i] 均为正数
if (target < 0) {
return;
}
if (start > end) {
return;
}
// 如果有值, 就可以直接 return
if (map[`${start}-${end}-${target}`]) {
return;
}
map[`${start}-${end}-${target}`] = true;
recursive(start + 1, end, target - nums[start], op + 1);
recursive(start, end - 1, target - nums[end], op + 1);
}
}
方案二: 反向思维 + 滑动窗口
边界条件太多了, 完全无法判断 start 和 end 到底什么时候该结束
这个时候的惯例就是加上一个 哨兵? 不行, 如果加 0 的话不影响 subSum 但是会会影响 end - start
官解其实也是单个滑动窗口, 不如我的好理解
var minOperations = function(nums, x) {
const sum = nums.reduce((acc, val) => acc + val, 0);
const target = sum - x;
if (target === 0) {
return nums.length;
}
let maxCount = 0;
let subSum = 0;
let left = 0;
let right = 0;
while (left <= right && right < nums.length) {
while (subSum < target) {
subSum += nums[right];
right++;
}
// 大于等于的瞬间
while (subSum >= target) {
if (subSum === target) {
maxCount = Math.max(maxCount, right - left);
subSum -= nums[left];
left++;
}
if (subSum > target) {
subSum -= nums[left];
left++;
}
}
}
let res = nums.length - maxCount;
return res === nums.length ? -1 : res;
};
let nums = [5,1,4,2,3], x = 6
console.log(minOperations(nums, x))